/**
 * rcache.h provide read cache function
 * author:  weizheng
 * date:    201703034
 * */

#ifndef __LSV_RCACHE__
#define __LSV_RCACHE__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "list.h"
#include "hash_table.h"
#include "lsv_volume_proto.h"
#include "lsv_types.h"
#include "lsv_conf.h"
#include "lsv_help.h"

/**
 * @file control parameter
 *
 * __CHUNK__ : if define, rcache load chunk per io
 *	if not define, rcache load page per io
 *
 * __RCACHE_MBUFFER__ : if define, rcache use buffer_t to construct data,
 *	in order to avoid one memcpy
 *	but now cant use, because of lich have the limit of buffer_t
 *
 * __HYBRID_CACHE__: if define, rcache load data accord raw_io_t
 *	when io> 4k load chunk, when io<=4k load page
 *
 */

//#define __RCACHE_MBUFFER__

#define HASH_BIT_LEN      (20)
#define HASH_KEY(off)     (((off) >> 12) & ((1 << HASH_BIT_LEN) - 1))
#define HASH_KEY1(off)    (((off)) & ((1 << HASH_BIT_LEN) - 1))
#define RCACHE_INDEX_SIZE (1 << 20)
#define __RCACHE_POLICY_4__

enum lsv_rcache_status {
        LSV_RCACHE_CHUNK,
        LSV_RCACHE_PAGE,
};

enum lsv_l2_cache_status{
	LSV_RCACHE_NULL,
        LSV_RCACHE_IN_MEM,
        LSV_RCACHE_IN_SSD,
        LSV_RCACHE_IN_HDD
};

enum lsv_rcache_unit_status{
	LSV_RCACHE_INIT,
	LSV_RCACHE_LOADING,
	LSV_RCACHE_SWAPING,
	LSV_RCACHE_REGC,
	LSV_RCACHE_IN_USE
};

typedef struct __lsv_rcache_iotrace{
        struct list_head list;
        lsv_u64_t lba;
        lsv_u32_t chunk_id;
        lsv_s32_t chunk_off;
        lsv_s32_t size;
        lsv_s32_t status;
}lsv_rcache_iotrace_unit;

/**
 * 出现在多个list里，需要通过list_entry区别访问
 */
typedef struct __lsv_rcache_page_unit {
        struct list_head page_lru_hook;  // page_list, LRU
        struct list_head index_list;     // page hash, lookup
        struct list_head chunk_list;     // chunk page hash, for gc, bitmap valueup

        lsv_rwlock_t rwlock;

        lsv_s8_t buf[LSV_PAGE_SIZE];

        lsv_u64_t lba;
        lsv_s32_t size;

        lsv_u32_t chunk_id;
        lsv_s32_t chunk_off;
        lsv_s32_t hit_count;
	lsv_s32_t status;
} lsv_rcache_page_unit;

typedef struct __lsv_rcache_chunk_unit {
        struct list_head chunk_lru_hook;
        struct list_head index_list;

        lsv_rwlock_t rwlock;

        lsv_s8_t buf[LSV_CHUNK_SIZE];

        lsv_u32_t chunk_id;
        //lsv_u32_t invalid_chunk_id;

        // 统计chunk内各页的访问
        lsv_s32_t hit_count;
	lsv_s32_t status;
        lsv_u64_t bmap[4];
} lsv_rcache_chunk_unit;

typedef struct __lsv_rcache_chunk_l2_unit {
        struct list_head l2_lru_hook;    // LRU: chunk_list
        struct list_head index_list;     // chunk hash index

        lsv_rwlock_t rwlock;

        lsv_u32_t chunk_id;
        lsv_u32_t l2_chunk_id;
        lsv_u32_t invalid_chunk_id;

        lsv_s32_t status;
        //lsv_u32_t hit_count;
        //lsv_u64_t vbmap[4];//标志各页是否有效
} lsv_rcache_chunk_l2_unit;

typedef struct __hash_index_head{
        struct list_head list;
} hash_index_head;

typedef struct __lsv_rcache {
        struct list_head page_lru;   // page LRU
        struct list_head chunk_lru;  // chunk LRU
        struct list_head l2_lru;

        struct list_head io_list;     // IO FIFO
        lsv_s32_t io_num;

        lsv_s32_t page_num;
        lsv_s32_t chunk_num;
        lsv_s32_t l2_chunk_num;

	lock_table_t lba_ltable;
	lock_table_t chunk_id_ltable;
	lock_table_t l2_chunk_id_ltable;

	co_cond_t lba_cond;
	co_cond_t chunk_id_cond;
	co_cond_t l2_cond;

        // <lba, pu list>
        hash_index_head *page_hash_index;

        // for gc, lsv_rcache_clean
        // hash: chunk_id -> pu list
        hash_index_head *page_chunk_hash_index;

        // <chunk_id, cu list>
        hash_index_head *chunk_hash_index;

        //<chunk_id, l2_chunk_list>
        hash_index_head *l2_chunk_hash_index;

        lsv_rwlock_t rwlock;

        uint64_t page_hit_cnt;
        uint64_t chunk_hit_cnt;
        uint64_t l2_hit_cnt;
        uint64_t page_load;
        uint64_t chunk_load;
        uint64_t l2_swap;
} lsv_rcache_t;

// ------------------------------------------------------------------------------------
// module exports
// ------------------------------------------------------------------------------------

// lsv_rcache.c

int lsv_rcache_init(lsv_volume_proto_t * volume_proto);
int lsv_rcache_release(lsv_volume_proto_t * volume_proto);

/**
 * @brief before bitmap, only search page hash.
 *
 * @param volume_proto
 * @param off
 * @param size
 * @param append_buf
 * @return
 */
int lsv_rcache_page_lookup(lsv_volume_proto_t *volume_proto, uint64_t off, lsv_s32_t size, buffer_t *append_buf);

/**
 * @brief after bitmap, search all cache layer (i.e. page, chunk, l2 ssd).
 *
 * @param volume_proto
 * @param chunk_id
 * @param chunk_offset
 * @param off
 * @param size
 * @param rio
 * @param append_buf
 * @return
 */
int lsv_rcache_lookup(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id,
                      lsv_s32_t chunk_offset, lsv_u64_t off, lsv_s32_t size,
                      const raw_io_t *rio,
                      buffer_t *append_buf);

/*
 * call load_pages*/
int lsv_rcache_lookup2(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id, lsv_s32_t chunk_offset,
                lsv_u64_t off, lsv_s32_t size, raw_io_t *rio,
                buffer_t *append_buf);
/**
 * @brief for bitmap valueup
 *
 * @param volume_proto
 * @param chunk_id
 * @param chunk_off
 * @return
 */
int lsv_rcache_page_clean(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id, lsv_s32_t chunk_off);


/**
 * @brief for gc etc
 *
 * @param volume_proto
 * @param chunk_id  if LSV_CHUNK_NULL, clean all chunks
 * @return
 */
int lsv_rcache_clean(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id);

int lsv_rcache_chunk_clean(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id);

int lsv_rcache_clean_all(lsv_volume_proto_t *volume_proto);


// ------------------------------------------------------------------------------------
// internals
// ------------------------------------------------------------------------------------

//lsv_rcach_index.c
int lsv_rcache_hash_index_init_i(hash_index_head ** rcache_hash_index, lsv_s32_t hash_table_size);

int lsv_rcache_hash_index_init(lsv_volume_proto_t *volume_proto);

int lsv_rcache_hash_index_destroy(lsv_volume_proto_t *volume_proto);

int lsv_rcache_hash_index_regc_all(lsv_volume_proto_t * volume_proto);
int lsv_rcache_hash_index_lookup(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id,
                                 lsv_s32_t chunk_off, lsv_u64_t off, lsv_s32_t size, buffer_t *append_buf);

// page hash

int lsv_rcache_page_hash_index_insert(lsv_volume_proto_t *volume_proto, lsv_rcache_page_unit *pu);
int lsv_rcache_page_hash_index_regc(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id, lsv_s32_t chunk_off);
int lsv_rcache_page_hash_index_regc_with_chunkid(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id);

int lsv_rcache_page_hash_index_lookup(lsv_volume_proto_t *volume_proto,
                lsv_u32_t chunk_id,
                lsv_s32_t chunk_off,
                lsv_u64_t lba,
                lsv_u32_t size,
                buffer_t *append_buf);

int lsv_rcache_load_page(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id, lsv_s32_t chunk_offset,
                         lsv_u64_t lba, lsv_s32_t size, buffer_t *append_buf);
int lsv_rcache_load_pages(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id,
                lsv_s32_t chunk_offset,lsv_u64_t off, lsv_s32_t size, buffer_t *append_buf);
 
// chunk hash below

int lsv_rcache_chunk_hash_index_insert(lsv_volume_proto_t *volume_proto, lsv_rcache_chunk_unit *cu);
int lsv_rcache_chunk_hash_index_regc(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id);

int lsv_rcache_chunk_hash_index_lookup(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id,
                lsv_s32_t chunk_off, lsv_u64_t off, lsv_s32_t size, buffer_t *append_buf);

// lsv_rcache_chunk.c
int lsv_rcache_load_chunk(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id, lsv_s32_t chunk_offset,
                          lsv_u64_t lba, lsv_s32_t size, buffer_t *append_buf);
int lsv_rcache_cu_reset(lsv_volume_proto_t *volume_proto, lsv_rcache_chunk_unit *cu);

// l2 hash below
int lsv_rcache_l2_cache_hash_index_regc(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id);

int lsv_rcache_l2_cache_hash_index_regc_with_mark(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id);

int lsv_rcache_l2_cache_hash_index_lookup(lsv_volume_proto_t * volume_proto, lsv_u32_t chunk_id, lsv_s32_t chunk_off,
					  lsv_u64_t off, lsv_s32_t size, buffer_t *append_buf);

int lsv_rcache_page_list_clear(lsv_volume_proto_t *volume_proto, lsv_rcache_page_unit *pu);



//lsv_rcache_rw.c

int lsv_rcache_read_data(lsv_volume_proto_t *volume_proto, lsv_s8_t * buf,
                lsv_u32_t chunk_id, lsv_s32_t chunk_off, lsv_s32_t size);
int lsv_rcache_write_data(lsv_volume_proto_t *volume_proto, lsv_s8_t * buf,
                lsv_u32_t chunk_id, lsv_s32_t chunk_off, lsv_s32_t size, lsv_u8_t type);

int lsv_rcache_chunk_load_judge(lsv_rcache_t * rcache, lsv_u64_t lba, lsv_u32_t chunk_id, lsv_s32_t chunk_off, lsv_s32_t size,
                const raw_io_t *rio);

int lsv_rcache_l2u_download_judge(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id);

int lsv_rcache_l2_cache_write_data(lsv_volume_proto_t *volume_proto, void *buf, lsv_u32_t chunk_id,
                lsv_s32_t chunk_off, lsv_s32_t size, lsv_s8_t type);

// lsv_rcache_page.c
int lsv_rcache_pu_insert(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id,
                lsv_s32_t chunk_offset,lsv_u64_t off, lsv_s32_t size, void * buf);

// from lsv_rcache_l2_cache.c
int lsv_rcache_ssd_chunk_init(lsv_volume_proto_t *volume_proto);
int lsv_rcache_ssd_chunk_free(lsv_volume_proto_t *volume_proto);

int lsv_rcache_lookup_l2_cache(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id,
                lsv_s32_t chunk_offset,lsv_u64_t off, lsv_s32_t size, buffer_t *append_buf);

// swap out chunk cache (memory)
int lsv_rcache_l2_cache_swap(lsv_volume_proto_t *volume_proto, lsv_rcache_chunk_unit *cu);


#endif
